#include "drv_oled.h"
#include <math.h>

/*SSD 1351 driver*/

static const uint8_t g_oled_gamma_lookup_tab[]=
{
0x02, 0x03, 0x04, 0x05,
0x06, 0x07, 0x08, 0x09,
0x0A, 0x0B, 0x0C, 0x0D,
0x0E, 0x0F, 0x10, 0x11,
0x12, 0x13, 0x15, 0x17,
0x19, 0x1B, 0x1D, 0x1F,
0x21, 0x23, 0x25, 0x27,
0x2A, 0x2D, 0x30, 0x33,
0x36, 0x39, 0x3C, 0x3F,
0x42, 0x45, 0x48, 0x4C,
0x50, 0x54, 0x58, 0x5C,
0x60, 0x64, 0x68, 0x6C,
0x70, 0x74, 0x78, 0x7D,
0x82, 0x87, 0x8C, 0x91,
0x96, 0x9B, 0xA0, 0xA5,
0xAA, 0xAF, 0xB4
};

struct oled_cmd_t{
  uint8_t paramLen;
  uint8_t cmd;
  const char *param;
};

static const struct oled_cmd_t g_init_cmd_tab[]={
  {1,0xFD,"\x12"}, /*0xFD,0x12*/
  {1,0xFd,"\xB1"},
  {0,0xAE},
  {1,0xB3,"\xF1"},
  
#if OLED_TYPE == OLED_128X96
  {1,0xCA,"\x5F"},
#else
  {1,0xCA,"\x7F"},
#endif
  
  {1,0xA2,"\x00"},
  {1,0xA1,"\x00"},
  
#if OLED_COLOR == OLED_COLOR_RGB
  {1,0xA0,"\xb4"}, //color equence is swapped C - B - A(RGB)
#else 
  {1,0xA0,"\xb0"}, //Color sequence: A - B - C(BGR)
#endif
  
  {1,0xB5,"\x00"},
  {1,0xAB,"\x01"},
  
  {3,0xb4,"\xA0\xB5\x55"},

#if OLED_TYPE == OLED_128X96
  {3,0xC1,"\xC8\x80\x8a"},
#else
  {3,0xC1,"\xC8\x80\xC8"},
#endif
  
  {1,0xC7,"\x0F"},
  {sizeof(g_oled_gamma_lookup_tab),0XB8,(const char*)g_oled_gamma_lookup_tab},
  {1,0xB1,"\x32"},
  {3,0xB2,"\xA4\x00\x00"},
  {1,0xBB,"\x17"},
  {1,0xB6,"\x01"},
  {1,0xBE,"\x05"},
  {0,0xA6},
};

#define bsp_oled_io_write_single_cmd_sync(cmd)  bsp_oled_io_write_cmd_sync(cmd,0,0)

void drv_oled_init()
{ 
  bsp_oled_io_init();
  
  for (uint8_t i=0;i<sizeof(g_init_cmd_tab)/sizeof(g_init_cmd_tab[0]);i++){
      bsp_oled_io_write_cmd_sync(
              g_init_cmd_tab[i].cmd,\
              (const uint8_t*)g_init_cmd_tab[i].param,\
              g_init_cmd_tab[i].paramLen);
  }
  
  /*
  TURN ON oled on user's application
  before turn on ,write G-RAM first!
  */
  
  /*Test show red color*/
  /*drv_oled_fill_screen(0x7F,0x00,0x00);*/
  drv_oled_fill_screen(0x00,0x00,0x00);
  drv_oled_exit_sleep();
}

__INLINE void drv_oled_enter_sleep()
{
  bsp_oled_io_write_single_cmd_sync(0xAE);
}

__INLINE void drv_oled_exit_sleep()
{
  bsp_oled_io_write_single_cmd_sync(0xAF);
}

void drv_oled_set_contrast(uint8_t val)
{
  int tmp;
  tmp=val;
  tmp*=15;
  tmp/=100;
  //val&=0x0F;
  val=tmp;
  bsp_oled_io_write_cmd_sync(0xC7,&val,1);
}

void drv_oled_set_write_area(uint8_t col,uint8_t col_end,uint8_t row,uint8_t row_end)
{
  uint8_t param[2];
  param[0]=col;
  param[1]=col_end;
  bsp_oled_io_write_cmd_sync(0x15,param,2);
  
  param[0]=row;
  param[1]=row_end;
  bsp_oled_io_write_cmd_sync(0x75,param,2);
}

/*
Call this function before drv_oled_write_ram.
*/
__INLINE void drv_oled_write_ram_enter()
{
  bsp_oled_io_write_single_cmd_sync(0x5c);
}

__INLINE void drv_oled_write_ram_sync(const uint8_t *pData,uint32_t len)
{
  bsp_oled_io_write_ram_sync(pData,len,1);
}

__INLINE void drv_oled_write_ram_async(const uint8_t *pData,uint32_t len)
{
  bsp_oled_io_write_ram_async(pData,len,1);
}

__INLINE uint32_t drv_oled_check_async_complete(void)
{
  return bsp_oled_iO_async_complete();
}

__INLINE void drv_oled_wait_async_complete(void)
{
  while(drv_oled_check_async_complete()==0){;}
}

/******************************************************************/
void drv_oled_update_screen_async(const uint8_t *pDataDMA,uint16_t len)
{
  drv_oled_set_write_area(0,IMAGE_WIDTH-1,0,IMAGE_HEIGHT-1);
  drv_oled_write_ram_enter();
  
  drv_oled_write_ram_async(pDataDMA,len);
}


#define PIXEL_BUFF_NUM     IMAGE_WIDTH
  
static uint8_t g_pixelBuffer[PIXEL_BUFF_NUM*3];

void drv_oled_set_color(uint8_t r,uint8_t g,uint8_t b)
{
  /*g_pixelBuffer is share. MUST wait complete before update*/
  drv_oled_wait_async_complete();
  
  for(uint16_t i=0;i<PIXEL_BUFF_NUM;i++)
  {
#if OLED_COLOR == OLED_COLOR_RGB
    g_pixelBuffer[i*3]=OLED_COLOR_MASK_R(r);
    g_pixelBuffer[i*3+1]=OLED_COLOR_MASK_G(g);
    g_pixelBuffer[i*3+2]=OLED_COLOR_MASK_B(b);
#else
    //swap R-G-B to B-G-R
    g_pixelBuffer[i*3]=OLED_COLOR_MASK_B(b);
    g_pixelBuffer[i*3+1]=OLED_COLOR_MASK_G(g);
    g_pixelBuffer[i*3+2]=OLED_COLOR_MASK_R(r);
#endif
  }
}

void drv_oled_draw_line(uint8_t x,uint8_t y,uint8_t width,uint8_t height)
{
  uint32_t u4PixelTotal;
  uint32_t u4WiteBytes;
  uint32_t u4PackCnt;
  
  drv_oled_set_write_area(x,x+width-1,y,y+height-1);
  drv_oled_write_ram_enter();
  
  u4PixelTotal=width*height;
  
  u4PackCnt = u4PixelTotal/PIXEL_BUFF_NUM;
  if(u4PackCnt>0){
    u4WiteBytes = 3*PIXEL_BUFF_NUM;
    bsp_oled_io_write_ram_async(g_pixelBuffer,u4WiteBytes,u4PackCnt);
  }
  
  /*Write the left pixels*/
  u4WiteBytes = (u4PixelTotal%PIXEL_BUFF_NUM)*3;
  if(u4WiteBytes>0){
    bsp_oled_io_write_ram_async(g_pixelBuffer,u4WiteBytes,1);
  }
}

void drv_oled_fill_screen(uint8_t r,uint8_t g,uint8_t b)
{
  drv_oled_set_color(r,g,b);
  drv_oled_draw_line(0,0,IMAGE_WIDTH,IMAGE_HEIGHT);
}

void drv_oled_draw_rec(uint8_t _x,uint8_t _y,uint8_t width,uint8_t height,uint8_t thickness)
{
  uint8_t x;
  uint8_t y;
  
  if(thickness>width || thickness>height){
    return;
  }
  
  /*----*/
  drv_oled_draw_line(_x,_y,width,thickness);

  /*____*/
  y=_y+height-thickness;
  x=_x;
  drv_oled_draw_line(x,y,width,thickness);
  
  
  if(height > 2*thickness){
    /*|  */
    y=_y+thickness;
    x=_x;
    drv_oled_draw_line(x,y,thickness,height-2*thickness);
    
    /*   |*/
    y=_y+thickness;
    x=_x+width-thickness;
    drv_oled_draw_line(x,y,thickness,height-2*thickness);
  }
}

void drv_oled_draw_circle(uint8_t x,uint8_t y,uint8_t r,uint8_t thickness)
{
  float dx;
  float dy;
  uint8_t x1,x2,y1,y2;
  uint8_t x1_Pre,y1_Pre;
  uint8_t x2_Pre,y2_Pre;
  
  dx=0;
  dy=0;
  
  if(thickness==1){
    drv_oled_draw_line(x-r,y,1,1);
    drv_oled_draw_line(x+r,y,1,1);
    drv_oled_draw_line(x,y+r,1,1);
    drv_oled_draw_line(x,y-r,1,1);
  }
  
  while(dx<r){
    
    dy=sqrt(r*r-dx*dx);
    
    x1=(uint8_t)round(x-dx);
    x2=(uint8_t)round(x+dx);
    y1=(uint8_t)round(y-dy);
    y2=(uint8_t)round(y+dy);
    
    if(x1!=x1_Pre ||  y1!=y1_Pre){
      drv_oled_draw_line(x1,y1,thickness,thickness);
    }
    
    if(x1!=x1_Pre ||  y2!=y2_Pre){
      drv_oled_draw_line(x1,y2,thickness,thickness);
    }
    
    if(x2!=x2_Pre ||  y1!=y1_Pre){
      drv_oled_draw_line(x2,y1,thickness,thickness);
    }
    
    if(x2!=x2_Pre ||  y2!=y2_Pre){
      drv_oled_draw_line(x2,y2,thickness,thickness);
    }
    
    x1_Pre = x1;
    y1_Pre = y1;
    x2_Pre = x2;
    y2_Pre = y2;
    
    /*if(x1==x1_Pre &&  y1==y1_Pre){
      //do nothing 
    }
    else{
      drv_oled_draw_line(x1,y1,thickness,thickness);
      drv_oled_draw_line(x1,y2,thickness,thickness);
      drv_oled_draw_line(x2,y1,thickness,thickness);
      drv_oled_draw_line(x2,y2,thickness,thickness);
      x1_Pre = x1;
      y1_Pre = y1;
    }*/
    
    dx+= 0.01f + (1.0f-dx/r)*0.15f;
  }
}
